package errorx

import (
	"fmt"
	"os"
	"runtime"
)

type Error struct {
	HttpStatus int             // http 状态码
	Code       int             // 自定义错误码
	msg        string          // 对外错误信息
	err        error           // 内部错误信息
	stack      []runtime.Frame // 错误发生的调用栈
}

func (e Error) Error() string {
	if e.msg != "" {
		return e.msg
	}

	return e.err.Error()
}

func (e Error) Unwrap() error {
	if len(e.stack) == 0 {
		return e.err
	}

	stack := e.stack[0]
	return fmt.Errorf("%s:%d %s", stack.File, stack.Line, e.err.Error())
}

func New(msg string, err error) error {
	return _new(0, 0, msg, err)
}

func _new(httpStatus, code int, msg string, err error) error {
	e := Error{
		HttpStatus: httpStatus,
		Code:       code,
		msg:        msg,
		err:        err,
	}

	pc, file, line, ok := runtime.Caller(2) // 获取上一级调用者的信息
	if !ok {
		return e
	}

	// 获取工作空间路径
	dir, _ := os.Getwd()
	fn := runtime.FuncForPC(pc)
	frame := runtime.Frame{
		File:     file[len(dir)+1:], // 去掉工作空间路径
		Line:     line,
		Function: fn.Name(),
	}

	e.stack = append(e.stack, frame)
	return e
}

// WithCode 增加自定义错误码
func WithCode(code int, msg string, err error) error {
	return _new(0, code, msg, err)
}

// WithHttpStatus 增加 http 状态码
func WithHttpStatus(httpStatus, code int, msg string, err error) error {
	return _new(httpStatus, code, msg, err)
}
